﻿using debugger;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using CyUSB;

namespace debugger
{
    public class Base : Form
    {
        public const Boolean HALF_TRANS = false;

        public string BaseType = "usb3";
        public int packageSize = 16 * 1024;

        USBDeviceList usbDevices;
        CyUSBDevice loopDevice = null;
        public CyBulkEndPoint inEndpoint = null;
        public CyBulkEndPoint outEndpoint = null;
        public CyBulkEndPoint inCmdEndpoint = null;
        public CyBulkEndPoint outCmdEndpoint = null;


        void usbDevices_DeviceRemoved(object sender, EventArgs e)
        {
            Console.WriteLine("usbDevices_DeviceRemoved");
            setDevice();
        }
        void usbDevices_DeviceAttached(object sender, EventArgs e)
        {
            Console.WriteLine("usbDevices_DeviceAttached");
            setDevice();
        }
        public bool isBasic;
        public void setDevice()
        {
            Console.WriteLine("setDevice start");
            //loopDevice = usbDevices[0x04b4, 0x1004] as CyUSBDevice;
            CyFX3Device progDevice = null;// usbDevices[0x04b4, 0x00f3] as CyFX2Device;
            loopDevice = null;
            foreach (USBDevice dev in usbDevices)
            {
                if (dev.VendorID == 0x04b4 && dev.ProductID == 0x00f3)
                {
                    progDevice = (CyFX3Device)dev;
                }
                if (dev.VendorID == 0x04b4 && dev.ProductID == 0x00f1)
                {
                    loopDevice = (CyUSBDevice)dev;
                }
            }
            if (loopDevice == null && progDevice == null) {
                Console.WriteLine("no dev");
            }
            if (loopDevice == null && progDevice != null)
            {
                //progDevice.LoadExternalRam(@"D:\MyCode.github\cy7c68013a\Bulkloop.firm.my\bulkloop.hex");
                //progDevice.LoadExternalRam(@"D:\MyCode.github\cy7c68013a\test1004slavefifo\bulkloop.hex");
                //progDevice.LoadExternalRam(@"debugger.cy7c68013a\cy7c68013a_slavefifo_firmwarepio\bulkloop.hex");
                //progDevice.LoadExternalRam(@"bulkloop.hex");
                Console.WriteLine("upload start");
                progDevice.DownloadFw(@"E:\cyWorkspace\SlaveFifoSync\Release\SlaveFifoSync.img", FX3_FWDWNLOAD_MEDIA_TYPE.RAM);
                Console.WriteLine("upload end");
            }


            if (loopDevice != null)
            {
                this.Text = loopDevice.FriendlyName;
                Console.WriteLine("found dev" + this.Text);
                //EndpointAddress 8:in from cy  0:out to cy
                outCmdEndpoint = loopDevice.EndPointOf(0x06) as CyBulkEndPoint;
                inCmdEndpoint = loopDevice.EndPointOf(0x86) as CyBulkEndPoint;
                outEndpoint = loopDevice.EndPointOf(0x02) as CyBulkEndPoint;
                inEndpoint = loopDevice.EndPointOf(0x82) as CyBulkEndPoint;
                if (outEndpoint.MaxPktSize != 512)
                {
                    //MessageBox.Show("MaxPktSize != 512");
                    Console.WriteLine("MaxPktSize = " + outEndpoint.MaxPktSize);
                }
                outEndpoint.TimeOut = 100000;
                inEndpoint.TimeOut = 100000;
                outCmdEndpoint.TimeOut = 1000;
                inCmdEndpoint.TimeOut = 1000;
                //
                //outEndpoint.Abort();
                //inEndpoint.Abort();
                //outCmdEndpoint.Abort();
                //inCmdEndpoint.Abort();
                //
                //outEndpoint.Reset();
                //inEndpoint.Reset();
                //outCmdEndpoint.Reset();
                //inCmdEndpoint.Reset();

                Thread.Sleep(10);

                while (true)
                {
                    int xferLen = 4;
                    byte[] inData = new byte[4];
                    bool bResult = inCmdEndpoint.XferData(ref inData, ref xferLen);
                    if (!bResult)
                    {
                        break;
                    }
                }
                try
                {
                    sendCmdRet(0, 0);
                    Random r = new Random();
                    uint v =  (uint)r.Next(int.MinValue, int.MaxValue);
                    uint ret = sendCmdRet(0xFF, v);
                    Console.WriteLine("base echo " + (ret == v + 1 ? "ok" : "ng") + " v:" + Util.getHex8(v) + " ret: " + Util.getHex8(ret));
                }
                catch (Exception ex)
                {
                    Console.WriteLine("base echo Exception");
                }

                try
                {
                    //uint temp = sendCmdRet(0x01, 123);
                    //isBasic = (temp & 0x10) != 0;
                    //temp = temp & 0x0F;
                    //if (temp != 0x0A)
                    //{
                    //    this.Text = "ERROR";
                    //    //Application.Exit();
                    //}
                    afterConnect();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception");
                }
            }
            Console.WriteLine("setDevice end");
        }

        public delegate void AfterConnect();

        AfterConnect afterConnect;
        public bool connect(AfterConnect afterConnect)
        {
            this.afterConnect = afterConnect;
            if (usbDevices != null)
            {
                usbDevices.Dispose();
            }
            byte DeviceMask = 0;

            DeviceMask |= CyConst.DEVICES_CYUSB;
            //DeviceMask |= MSCDeviceBox.Checked ? CyConst.DEVICES_MSC : (byte)0;
            //DeviceMask |= HIDDeviceBox.Checked ? CyConst.DEVICES_HID : (byte)0;

            Console.WriteLine("init start");
            usbDevices = new USBDeviceList(DeviceMask);
            Console.WriteLine("init end");

            usbDevices.DeviceRemoved += new EventHandler(usbDevices_DeviceRemoved);
            usbDevices.DeviceAttached += new EventHandler(usbDevices_DeviceAttached);

            //The below function sets the device with particular VID and PId and searches for the device with the same VID and PID.
            setDevice();
            return true;
        }
        private uint readFromPort()
        {
            int size = 4;
            bool bResult;
            byte[] inData = new byte[4];
            Stopwatch sw = new Stopwatch();
            sw.Start();
            while (true)
            {
                int xferLen = size;
                bResult = inCmdEndpoint.XferData(ref inData, ref xferLen);
                if (bResult)
                {
                    break;
                }
                if (sw.ElapsedMilliseconds > 1000)
                {
                    throw new Exception();
                }
            }
            uint ret = 0;
            ret |= (uint)inData[0] << 0;
            ret |= (uint)inData[1] << 8;
            ret |= (uint)inData[2] << 16;
            ret |= (uint)inData[3] << 24;
            return ret;
        }

        byte cmd_count = 0;
        public uint sendCmdRet(byte cmd, uint? udata = null)
        {
            int size = 8;
            bool bResult;
            byte[] outData = new byte[8];
            outData[0] = (byte)cmd;
            outData[1] = cmd_count++;
            if (udata == null)
            {
                udata = 0;
            }
            outData[4] = (byte)(udata >> 0);
            outData[5] = (byte)(udata >> 8);
            outData[6] = (byte)(udata >> 16);
            outData[7] = (byte)(udata >> 24);
            int xferLen = size;
            bResult =  outCmdEndpoint.XferData(ref outData, ref xferLen);
            if (!bResult)
            {
                throw new Exception();
            }
            uint val = 0;
            val = readFromPort();
            return val;
        }

        public void sendCmd(byte cmd, uint? udata = null)
        {
            sendCmdRet(cmd, udata);
        }
        public void sendCmdNoRes(byte cmd, uint? udata = null)
        {
            sendCmdRet(cmd, udata);
        }
        
        
        public bool receive(byte[] data, uint length)//read
        {
            sendCmd(0x05);

            bool bResult;

            byte[] temp;
            int xferLen;
            xferLen = (int)length;
            if (HALF_TRANS)
            {
                xferLen = (int)length * 2;
                temp = new byte[data.Length * 2];
                bResult = inEndpoint.XferData(ref temp, ref xferLen);
            }
            else
            {
                bResult = inEndpoint.XferData(ref data, ref xferLen);
            }
            if (!bResult)
            {
                throw new Exception();
            }

            while (true)
            {
                uint mem_status = sendCmdRet(0x2D);
                if (mem_status == 0)
                {
                    break;
                }
            }
            Thread.Sleep(10);
            sendCmd(0x04);
            sendCmd(0x03);
            sendCmd(0x02);
            if (HALF_TRANS)
            {
                for (int i = 0; i < data.Length; i += 2)
                {
                    data[i + 0] = temp[i * 2 + 0];
                    data[i + 1] = temp[i * 2 + 1];
                }
            }
            return true;
        }
        public bool send(byte[] data, uint length)//write
        {
            sendCmd(0x05);
            int xferLen;
            bool bResult;

            xferLen = (int)length;
            byte[] temp;
            if (HALF_TRANS)
            {
                temp = new byte[data.Length * 2];
                for (int i = 0; i < data.Length; i += 2)
                {
                    temp[i * 2 + 0] = data[i + 0];
                    temp[i * 2 + 1] = data[i + 1];
                }
                data = temp;
                xferLen = (int)length * 2;
            }
            bResult = outEndpoint.XferData(ref data, ref xferLen);
            if (!bResult)
            {
                throw new Exception();
            }

            while (true)
            {
                uint mem_status = sendCmdRet(0x2D);
                if (mem_status == 0)
                {
                    break;
                }
            }
            Thread.Sleep(10);
            sendCmd(0x04);
            sendCmd(0x03);
            sendCmd(0x02);
            return true;
        }
        byte[] data2 = new byte[1024];

        public const byte VGA_CONTROL_BY_DEBUGGER = 0x06;

        public bool fast = true;



        public void getVGA(byte[] data, uint baseAddr, bool fast341, bool reset)//read
        {
            uint frameSize = 1024 * (1024 + 512);
            //frameSize = 1024 * 1024;

            sendCmd(Form1.SET_DEBUG_ADDRESS, baseAddr);
            sendCmd(Form1.SET_DEBUG_LENGTH, frameSize);
            sendCmdNoRes(Form1.VGA_TRANS);


            sendCmd(0x05);

            bool bResult;

            int xferLen;

            xferLen = (int)frameSize;

            byte[] temp;
            if (HALF_TRANS)
            {
                xferLen = (int)frameSize * 2;
                temp = new byte[data.Length * 2];
                bResult = inEndpoint.XferData(ref temp, ref xferLen);
            }
            else
            {
                bResult = inEndpoint.XferData(ref data, ref xferLen);
            }
            if (!bResult)
            {
                throw new Exception();
            }
            sendCmd(0x04);
            sendCmd(0x03);
            sendCmd(0x02);

            if (HALF_TRANS)
            {
                for (int i = 0; i < data.Length; i += 2)
                {
                    data[i + 0] = temp[i * 2 + 0];
                    data[i + 1] = temp[i * 2 + 1];
                }
            }
        }
        /*
        public bool fpga2pc(ref byte[] buff2, ref int xferLen)
        {
            sendCmd(0x07, 1);//transfer_dir
            sendCmd(0x08, 1);//transfer_req
            bool ret = inEndpoint.XferData(ref buff2, ref xferLen);
            Thread.Sleep(10);
            sendCmd(0x08, 0);//transfer_req
            return ret;
        }


        public bool pc2fpga(ref byte[] buff2, ref int xferLen)
        {
            sendCmd(0x07, 0);//transfer_dir
            sendCmd(0x08, 1);//transfer_req
            bool ret = outEndpoint.XferData(ref buff2, ref xferLen);
            Thread.Sleep(10);
            sendCmd(0x08, 0);//transfer_req
            return ret;
        }
        */
        


        public bool fpga2pcReq()
        {
            sendCmd(0x07, 1);//transfer_dir
            sendCmd(0x08, 1);//transfer_req
            Thread.Sleep(100);
            sendCmd(0x08, 0);//transfer_req
            return true;
        }


        public bool pc2fpgaReq()
        {
            sendCmd(0x07, 0);//transfer_dir
            sendCmd(0x08, 1);//transfer_req
            Thread.Sleep(100);
            sendCmd(0x08, 0);//transfer_req
            return true;
        }



        public bool fpga2pcTrans(ref byte[] buff2, ref int xferLen)
        {
            bool ret = inEndpoint.XferData(ref buff2, ref xferLen);
            return ret;
        }


        public bool pc2fpgaTrans(ref byte[] buff2, ref int xferLen)
        {
            bool ret = outEndpoint.XferData(ref buff2, ref xferLen);
            return ret;
        }









        //public bool init()
        //{
        //    byte[] buff = new byte[16384];
        //    Random r = new Random();
        //    r.NextBytes(buff);
        //    int xferLen = 16384;
        //    bool ret = outEndpoint.XferData(ref buff, ref xferLen);
        //    sendCmd(0x07, 0);//transfer_dir
        //    sendCmd(0x08, 1);//transfer_req
        //    Thread.Sleep(100);
        //    sendCmd(0x08, 0);//transfer_req
        //
        //
        //
        //    int errCnt = 0;
        //    for (int i = 0; i < xferLen; i += 4)
        //    {
        //        sendCmd(Form1.SET_DEBUG_ADDRESS, (uint)i / 4);
        //        uint v = sendCmdRet(0x26, 0);
        //        uint v1 = 0;
        //        if (buff != null)
        //        {
        //            v1 |= (((uint)buff[i + 0]) & 0xFF);
        //            v1 |= (((uint)buff[i + 1]) & 0xFF) << 8;
        //            v1 |= (((uint)buff[i + 2]) & 0xFF) << 16;
        //            v1 |= (((uint)buff[i + 3]) & 0xFF) << 24;
        //        }
        //        if (v != v1)
        //        {
        //            errCnt++;
        //            if (errCnt > 32)
        //            {
        //                break;
        //            }
        //            Console.WriteLine(i + "," + Util.getHex8(v) + "," + Util.getHex8(v1));
        //        }
        //        //uint f = mainForm.sendCmdRet(0x27, 0);
        //        //Console.WriteLine(i + "," + Util.getHex8(f));
        //    }
        //
        //
        //
        //    return ret;
        //}

    }
}
